package com.hzya.frame.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.nifi.logging.ComponentLog;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;

/**
 * 线程安全的API Token管理器
 * 遵循“一次配置，多次使用”的原则
 */
public class ApiTokenManager {

    // ------------------- 单例实现 -------------------
    private static volatile ApiTokenManager instance;

    private ApiTokenManager() {
    }

    public static ApiTokenManager getInstance() {
        if (instance == null) {
            synchronized (ApiTokenManager.class) {
                if (instance == null) {
                    instance = new ApiTokenManager();
                }
            }
        }
        return instance;
    }

    // ------------------- 配置属性 -------------------
    private String tokenUrl;
    private String refreshUrl;
    private String appId;
    private String appSecret;
    private ComponentLog logger;

    private String cachedToken;
    private long expiryTimestamp;
    private volatile boolean isConfigured = false; // 配置状态标志

    private static final long REFRESH_BUFFER_MS = 10 * 60 * 1000L; // 10分钟容差
    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 【新增】配置Token管理器，且只允许配置一次。
     * 建议在应用启动时（如NiFi处理器的@OnScheduled方法中）调用。
     *
     * @param tokenUrl   获取Token的URL
     * @param refreshUrl 刷新Token的URL
     * @param appId      应用ID
     * @param appSecret  应用密钥
     * @param logger     用于记录日志的NiFi ComponentLog
     */
    public synchronized void configure(String tokenUrl, String refreshUrl, String appId, String appSecret, ComponentLog logger) {
        if (isConfigured) {
            logger.warn("Token管理器已被配置，本次配置请求将被忽略。");
            return;
        }
        this.tokenUrl = tokenUrl;
        this.refreshUrl = refreshUrl;
        this.appId = appId;
        this.appSecret = appSecret;
        this.logger = logger;
        this.isConfigured = true;
        logger.info("Token管理器配置成功。");
    }

    /**
     * 【简化】对外提供的唯一公共方法，不再需要传递参数。
     *
     * @return 有效的Bearer Token字符串，格式为 "Bearer <token>"
     * @throws Exception 如果获取或刷新失败
     */
    public synchronized String getValidToken() throws Exception {
        if (!isConfigured) {
            throw new IllegalStateException("Token管理器尚未配置，请先调用configure方法进行初始化。");
        }

        long now = System.currentTimeMillis();

//        if (cachedToken == null) {
//            logger.info("Token为空，首次获取新Token...");
//            fetchNewToken();
//        } else if (now >= expiryTimestamp) {
//            logger.info("Token已过期或即将过期（在10分钟容差内），正在刷新Token...");
//            refreshToken();
//        } else {
//            logger.debug("从缓存中获取有效Token。");
//        }

        if (cachedToken == null) {
            logger.info("Token为空，首次获取新Token...");
            fetchNewToken();
        } else if (now >= expiryTimestamp) {
            logger.info("Token已过期或即将过期（在10分钟容差内），正在重新获取Token...");
//            refreshToken();
            fetchNewToken();
        } else {
            logger.debug("从缓存中获取有效Token。");
        }

        //2025-09-11 16:34:27


        return "Bearer " + this.cachedToken;
    }

    // 内部方法现在直接使用类成员变量，不再需要传递参数
    private void fetchNewToken() throws Exception {
        HttpURLConnection connection = null;
        try {
            URL url = new URL(this.tokenUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
            connection.setDoOutput(true);

            String requestBody = String.format("{\"app_id\":\"%s\",\"app_secret\":\"%s\",\"grant_type\":\"client_credentials\"}", this.appId, this.appSecret);

            try (OutputStream os = connection.getOutputStream()) {
                os.write(requestBody.getBytes(StandardCharsets.UTF_8));
            }

            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                String responseBody = readResponse(connection.getInputStream());
                processTokenResponse(responseBody);
            } else {
                String errorBody = readResponse(connection.getErrorStream());
                throw new RuntimeException("获取Token失败，HTTP状态码: " + responseCode + ", 响应: " + errorBody);
            }
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    private void refreshToken() throws Exception {
        if (this.cachedToken == null) {
            throw new IllegalStateException("无法刷新Token，因为当前没有有效的Token。");
        }
        HttpURLConnection connection = null;
        try {
            URL url = new URL(this.refreshUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Authorization", "Bearer " + this.cachedToken);

            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                String responseBody = readResponse(connection.getInputStream());
                processTokenResponse(responseBody);
            } else {
                String errorBody = readResponse(connection.getErrorStream());
                this.cachedToken = null;
                this.expiryTimestamp = 0;
                throw new RuntimeException("刷新Token失败，HTTP状态码: " + responseCode + ", 响应: " + errorBody);
            }
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    private void processTokenResponse(String responseBody) throws IOException {
        JsonNode responseJson = objectMapper.readTree(responseBody);
        String code = responseJson.path("code").asText();
        if (!"0".equals(code)) {
            String msg = responseJson.path("msg").asText("未知错误");
            throw new RuntimeException("获取Token API返回业务错误: code=" + code + ", msg=" + msg);
        }
        JsonNode dataNode = responseJson.path("data");
        this.cachedToken = dataNode.path("token").asText();
        long expiresInSeconds = dataNode.path("expires").asLong();
        if (this.cachedToken == null || this.cachedToken.isEmpty() || expiresInSeconds <= 0) {
            throw new RuntimeException("从API返回的JSON中未能获取到有效的Token或expires。响应: " + responseBody);
        }
        this.expiryTimestamp = System.currentTimeMillis() + (expiresInSeconds * 1000L) - REFRESH_BUFFER_MS;
        logger.info("成功获取或刷新Token，Token将在 {} 秒后进行下一次刷新。", (this.expiryTimestamp - System.currentTimeMillis()) / 1000);
    }

    private String readResponse(InputStream inputStream) throws IOException {
        if (inputStream == null) return "No response body.";
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
            return reader.lines().collect(Collectors.joining("\n"));
        }
    }
}